package com.cskaoyan.thread02.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
         Lock锁对象 VS synchoronized 锁对象

           区别
           1.  synchronized 锁对象，只提供了用来模拟锁状态的标志位(加锁和释放锁)，
              但是加锁和释放锁的行为，都是由jvm隐式完成(和synchronized 锁对象没关系)，
              所以synchronized 锁对象不是一把完整的锁

           2.一个Lock对象，就代表一把锁，而且还是一把完整的锁，
             Lock对象，它如果要实现加锁和释放锁，不需要synchronized关键字配合，它自己就可以完成
             Lock(接口): lock() 加锁
                        unlock 释放锁

           3. 两种锁对象，实现完全不同的
        联系： 都可以实现线程同步
            1.  synchronized(锁对象) {
                  需要同步的代码
                }

            2.  lock.lock()
                需要同步的代码
                lock.unlock()
            学习了Lock锁对象之后，我们就有两种方式，构造同步代码块，从而实现线程通过(构造原子操作)，
            实际开发的时，使用哪种方式呢？ 推荐 synchronized代码块
             1. 两种方式，实现线程同步，效果相同，但是 使用synchronized代码块的方式要简单的多

             2. 虽然说在jdk早期版本中，两种方式加锁和释放锁，确实有效率上的差别，Lock锁机制加锁释放锁
                效率高一些，但是，在今天的jdk中，两种方式加锁和释放锁的效率已经相差无几了


 */
public class Demo1 {

    public static void main(String[] args) {
        // 创建售票任务对象
        SalesTask salesTask = new SalesTask();

        Thread firstWindow = new Thread(salesTask, "first");
        Thread secondWindow = new Thread(salesTask, "second");
        Thread thirdWindow = new Thread(salesTask, "third");

        // 启动三个线程，模拟三个窗口售票
        firstWindow.start();
        secondWindow.start();
        thirdWindow.start();
    }
}


class SalesTask implements Runnable {

    //待售卖的100张票
    // 普通成员变量
    int tickets = 100;

    // Lock锁对象
    Lock lockObj = new ReentrantLock();

    // 任意对象作为synchronized代码块的锁对象
    Object obj = new Object();

    @Override
    public void run() {
        //  售卖100张票
        while (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 实现线程同步
//            synchronized (obj) {
//                // 判断是否有票  double check
//                if (tickets > 0) {
//                    System.out.println(Thread.currentThread().getName() + "售出了第" + tickets-- + "张票");
//                }
//            }

            /*
                1.在这里让线程对lock锁对象
                2. lock锁对象，同时只能被一个线程加锁
                3. 加锁失败的线程也会阻塞等待
            */

            lockObj.lock();
            try {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "售出了第" + tickets-- + "张票");
                }
            } finally {
                // 释放锁
                lockObj.unlock();
            }


        }
    }

}
